/* Simple HTTP Server Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include "include.h"

static const char *TAG = "ap_http_server";

extern xQueueHandle demoks_access_net_event;
static httpd_handle_t server = NULL;
static ip4_addr_t s_ip_addr;
static uint8_t wifi_distribution_count = 0;
static xQueueHandle event_wifi_distribution = NULL;

bool wifi_distribution_running = false;

static wifi_scan_config_t scanConf = {
    .ssid = NULL,
    .bssid = NULL,
    .channel = 0,
    .show_hidden = 0}; // 定义scanConf结构体，供函数esp_wifi_scan_start调用

static char *get_msg(demoks_access_nat_code_t n)
{
    switch (n)
    {
    case DEMOSK_ACCESS_NAT_CODE_TOKEN_INVALID:
        return "身份验证无效";
    case DEMOSK_ACCESS_NAT_CODE_GET_SSID_FAIL:
        return "搜索WIFI热点失败";
    case DEMOSK_ACCESS_NAT_CODE_GET_SSID_EMPTY:
        return "搜索不到附近的SSID";
    case DEMOSK_ACCESS_NAT_CODE_ERROR_JSON:
        return "请检查JSON格式和协议参数，当前固件版本（" DEMOKS_SYSTEM_MAIN_FIRM_VER "）";
    case DEMOSK_ACCESS_NAT_CODE_WIFI_DIS_FAIL:
        return "请检查passwd是否正确，当前固件版本（" DEMOKS_SYSTEM_MAIN_FIRM_VER "）";
    default:
        return "";
    }
}

/* HTTP GET 获取wifi搜索到的ssid列表 */
esp_err_t wifi_ssid_get_handler(httpd_req_t *req)
{
    char *buf;
    size_t buf_len;
    cJSON *json_data = NULL;
    cJSON *json_array = cJSON_CreateArray();
    int8_t code = DEMOSK_ACCESS_NAT_CODE_TOKEN_INVALID;
    char *msg = "";

    json_data = cJSON_CreateObject();
    /** 鉴权操作 */
    buf_len = httpd_req_get_hdr_value_len(req, "x-access-token") + 1;
    if (buf_len > 1)
    {
        buf = malloc(buf_len);
        if (httpd_req_get_hdr_value_str(req, "x-access-token", buf, buf_len) == ESP_OK)
        {
            // 鉴权在这里写
            code = CHECK_X_ACCESS_TOKEN(buf);
            // 鉴权在这里写
            ESP_LOGI(TAG, "Found header => x-access-token: %s", buf);
        }
        free(buf);
    }

    if (code != DEMOSK_ACCESS_NAT_CODE_SUCCESS)
    {
        msg = get_msg(code);
        goto send;
    }

    // 设置返回数据类型
    httpd_resp_set_type(req, "application/json");

    // 阻塞来获取附近的WIFI信息
    int re = esp_wifi_scan_start(&scanConf, 1);

    if (re == ESP_OK)
    {
        ESP_LOGI(TAG, "ok!!!");
        uint16_t apCount = 0;
        esp_wifi_scan_get_ap_num(&apCount);
        if (apCount == 0)
        {
            ESP_LOGI(TAG, "Nothing AP found");
            code = DEMOSK_ACCESS_NAT_CODE_GET_SSID_EMPTY;
            msg = get_msg(code);
        }
        else
        {
            // 定义一个wifi_ap_record_t的结构体的链表空间
            wifi_ap_record_t *list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount);
            // 获取上次扫描中找到的AP列表。
            ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, list));

            for (int i = 0; i < apCount; i++)
            {
                char *authmode;
                switch (list[i].authmode)
                {
                case WIFI_AUTH_OPEN:
                    authmode = "WIFI_AUTH_OPEN";
                    break;
                case WIFI_AUTH_WEP:
                    authmode = "WIFI_AUTH_WEP";
                    break;
                case WIFI_AUTH_WPA_PSK:
                    authmode = "WIFI_AUTH_WPA_PSK";
                    break;
                case WIFI_AUTH_WPA2_PSK:
                    authmode = "WIFI_AUTH_WPA2_PSK";
                    break;
                case WIFI_AUTH_WPA_WPA2_PSK:
                    authmode = "WIFI_AUTH_WPA_WPA2_PSK";
                    break;
                default:
                    authmode = "Unknown";
                    continue;
                }
                cJSON *array_item = cJSON_CreateObject();
                cJSON_AddStringToObject(array_item, "ssid", (char *)list[i].ssid);
                char str[32];
                sprintf(&str, "%02X:%02X:%02X:%02X:%02X:%02X",
                        list[i].bssid[0],
                        list[i].bssid[1],
                        list[i].bssid[2],
                        list[i].bssid[3],
                        list[i].bssid[4],
                        list[i].bssid[5]);
                cJSON_AddStringToObject(array_item, "bssid", str);
                cJSON_AddNumberToObject(array_item, "rssi", list[i].rssi);
                cJSON_AddStringToObject(array_item, "authmode", authmode);
                cJSON_AddItemToArray(json_array, array_item);
            }
            free(list);
        }
    }
    else
    {
        ESP_LOGE(TAG, "ERROR %d", re);
        code = DEMOSK_ACCESS_NAT_CODE_GET_SSID_FAIL;
        msg = get_msg(code);
    }

send:

    cJSON_AddNumberToObject(json_data, "code", code);
    cJSON_AddStringToObject(json_data, "msg", msg);
    cJSON_AddItemToObject(json_data, "list", json_array);

    /* 返回数据 */
    const char *resp_str = cJSON_PrintUnformatted(json_data);
    httpd_resp_send(req, resp_str, strlen(resp_str));
    /** 删除数据 */
    cJSON_Delete(json_data);
    /* After sending the HTTP response the old HTTP request
     * headers are lost. Check if HTTP request headers can be read now. */
    if (httpd_req_get_hdr_value_len(req, "Host") == 0)
    {
        ESP_LOGI(TAG, "Request headers lost");
    }
    return ESP_OK;
}

/* HTTP GET 获取设备信息 */
esp_err_t demoks_info_get_handler(httpd_req_t *req)
{
    char *buf;
    size_t buf_len;
    cJSON *json_data = NULL;
    cJSON *json_array = cJSON_CreateArray();
    int8_t code = DEMOSK_ACCESS_NAT_CODE_TOKEN_INVALID;
    char *msg = "";

    json_data = cJSON_CreateObject();
    /** 鉴权操作 */
    buf_len = httpd_req_get_hdr_value_len(req, "x-access-token") + 1;
    if (buf_len > 1)
    {
        buf = malloc(buf_len);
        if (httpd_req_get_hdr_value_str(req, "x-access-token", buf, buf_len) == ESP_OK)
        {
            // 鉴权在这里写
            code = CHECK_X_ACCESS_TOKEN(buf);
            // 鉴权在这里写
            ESP_LOGI(TAG, "Found header => x-access-token: %s", buf);
        }
        free(buf);
    }

    if (code != DEMOSK_ACCESS_NAT_CODE_SUCCESS)
    {
        msg = get_msg(code);
        goto send;
    }

    // 设置返回数据类型
    httpd_resp_set_type(req, "application/json");

send:

    cJSON_AddNumberToObject(json_data, "code", code);                                /** 响应码 */
    cJSON_AddStringToObject(json_data, "msg", msg);                                  /** 响应信息 */
    cJSON_AddStringToObject(json_data, "deviceId", TENCENT_MQTTS_DEVICE_NAME);       /** 设备名称 */
    cJSON_AddStringToObject(json_data, "deviceModel", DEMOKS_SYSTEM_DEVICE_MODEL);   /** 版本型号 */
    cJSON_AddStringToObject(json_data, "mainFirmVer", DEMOKS_SYSTEM_MAIN_FIRM_VER);  /** 固件版本 */
    cJSON_AddStringToObject(json_data, "modelCode", DEMOKS_SYSTEM_MODEL_CODE);       /** modelCode */
    cJSON_AddStringToObject(json_data, "publicKey", demoks_system_get_public_key()); /** 通讯公钥 */

    /* 返回数据 */
    const char *resp_str = cJSON_PrintUnformatted(json_data);
    httpd_resp_send(req, resp_str, strlen(resp_str));
    /** 删除数据 */
    cJSON_Delete(json_data);
    /* After sending the HTTP response the old HTTP request
     * headers are lost. Check if HTTP request headers can be read now. */
    if (httpd_req_get_hdr_value_len(req, "Host") == 0)
    {
        ESP_LOGI(TAG, "Request headers lost");
    }
    return ESP_OK;
}

httpd_uri_t get_wifi_ssid = {
    .uri = "/wifi/ssid",
    .method = HTTP_GET,
    .handler = wifi_ssid_get_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx = NULL};

httpd_uri_t get_demoks_info = {
    .uri = "/demoks/info",
    .method = HTTP_GET,
    .handler = demoks_info_get_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx = NULL};

static void on_wifi_disconnect(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data)
{
    if (!wifi_distribution_running)
        return;
    if (wifi_distribution_count < DEMOSK_ACCESS_NAT_HTTP_SERVER_MAX_DIS)
    {
        wifi_distribution_count++;
        system_event_sta_disconnected_t *event = (system_event_sta_disconnected_t *)event_data;

        ESP_LOGI(TAG, "Wi-Fi disconnected!!");
        ESP_LOGW(TAG, "重试连接 [%d/%d]", wifi_distribution_count, DEMOSK_ACCESS_NAT_HTTP_SERVER_MAX_DIS);
        ESP_ERROR_CHECK(esp_wifi_connect());
    }
    else
    {
        wifi_distribution_event_code_t event_code = WIFI_DISTRIBUTION_EVENT_CODE_FAIL;
        // 连接WIFI失败次数达到设定的上限，抛出配网失败
        xQueueSend(event_wifi_distribution, (wifi_distribution_event_code_t *)&event_code, portMAX_DELAY);
    }
}

static void on_got_ip(void *arg, esp_event_base_t event_base,
                      int32_t event_id, void *event_data)
{
    if (!wifi_distribution_running)
        return;
    ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
    memcpy(&s_ip_addr, &event->ip_info.ip, sizeof(s_ip_addr));
    ESP_LOGI(TAG, "%d.%d.%d.%d",
             ip4_addr1_val(s_ip_addr),
             ip4_addr2_val(s_ip_addr),
             ip4_addr3_val(s_ip_addr),
             ip4_addr4_val(s_ip_addr));
    // 发送WIFI连接成功代码
    wifi_distribution_event_code_t event_code = WIFI_DISTRIBUTION_EVENT_CODE_SUCCESS;
    // 连接WIFI失败次数达到设定的上限，抛出配网失败
    xQueueSend(event_wifi_distribution, (wifi_distribution_event_code_t *)&event_code, portMAX_DELAY);
}

/* HTTP POST 连接WIFI */
esp_err_t wifi_distribution_post_handler(httpd_req_t *req)
{
    char ip[64];
    char buf[100];
    char *buf0;
    int ret, remaining = req->content_len;
    char dest[512];
    memset(dest, 0, sizeof(dest));
    int8_t code = DEMOSK_ACCESS_NAT_CODE_TOKEN_INVALID;
    char *msg = "";
    size_t buf_len;
    cJSON *json, *json_data;
    json_data = cJSON_CreateObject();
    wifi_distribution_count = 0;
    wifi_distribution_running = true;
    /** 鉴权操作 */
    buf_len = httpd_req_get_hdr_value_len(req, "x-access-token") + 1;
    if (buf_len > 1)
    {
        buf0 = malloc(buf_len);
        if (httpd_req_get_hdr_value_str(req, "x-access-token", buf0, buf_len) == ESP_OK)
        {
            // 鉴权在这里写
            code = CHECK_X_ACCESS_TOKEN(buf0);
            // 鉴权在这里写
            ESP_LOGI(TAG, "Found header => x-access-token: %s", buf0);
        }
        free(buf0);
    }

    if (code != DEMOSK_ACCESS_NAT_CODE_SUCCESS)
    {
        msg = get_msg(code);
        goto send;
    }

    while (remaining > 0)
    {
        /* Read the data for the request */
        if ((ret = httpd_req_recv(req, buf,
                                  MIN(remaining, sizeof(buf)))) <= 0)
        {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT)
            {
                /* Retry receiving if timeout occurred */
                continue;
            }
            return ESP_FAIL;
        }

        /* Send back the same data */
        // httpd_resp_send_chunk(req, buf, ret);
        remaining -= ret;

        ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
        ESP_LOGI(TAG, "%.*s", ret, buf);
        ESP_LOGI(TAG, "====================================");

        strncpy(&dest, buf, ret);

        json = cJSON_Parse(&dest);

        if (NULL == json)
        {
            ESP_LOGW(TAG, "Warn before: [%s]", cJSON_GetErrorPtr());
            code = DEMOSK_ACCESS_NAT_CODE_ERROR_JSON;
            msg = get_msg(code);
        }
        else if (!cJSON_IsObject(json))
        {
            code = DEMOSK_ACCESS_NAT_CODE_ERROR_JSON;
            msg = get_msg(code);
        }
        else
        {
            cJSON *json_passwd, *json_ssid;

            json_passwd = cJSON_GetObjectItem(json, "passwd");
            json_ssid = cJSON_GetObjectItem(json, "ssid");
            if (!cJSON_IsString(json_passwd) || !cJSON_IsString(json_ssid))
            {
                code = DEMOSK_ACCESS_NAT_CODE_ERROR_JSON;
                msg = get_msg(code);
            }
            else
            {
                sprintf(json_ssid->valuestring,"%s","Lonon-F3");
                sprintf(json_passwd->valuestring,"%s","13630055772");    

                ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
                ESP_LOGI(TAG, "ssid %s", json_ssid->valuestring);
                ESP_LOGI(TAG, "passwd %s", json_passwd->valuestring);
                ESP_LOGI(TAG, "====================================");
                // 配置 ssid 和 password
                wifi_config_t wifi_config = {0};
                strncpy((char *)&wifi_config.sta.ssid, json_ssid->valuestring, 32);
                strncpy((char *)&wifi_config.sta.password, json_passwd->valuestring, 32);

                ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
                esp_wifi_disconnect();
                ESP_ERROR_CHECK(esp_wifi_connect());
                wifi_distribution_event_code_t event_code = WIFI_DISTRIBUTION_EVENT_CODE_NONE;
                demoks_access_net_event_code_t event_code_0 = DEMOSK_ACCESS_NAT_EVENT_CODE_NONE;
                // 等待连接
                xQueueReceive(event_wifi_distribution, (wifi_distribution_event_code_t *)&event_code, portMAX_DELAY);
                switch (event_code)
                {
                case WIFI_DISTRIBUTION_EVENT_CODE_SUCCESS: /** 连接WIFI成功 */
                    memset(ip, 0, sizeof(ip));
                    sprintf(&ip, "连接wifi成功，[%d.%d.%d.%d]",
                            ip4_addr1_val(s_ip_addr),
                            ip4_addr2_val(s_ip_addr),
                            ip4_addr3_val(s_ip_addr),
                            ip4_addr4_val(s_ip_addr));
                    msg = ip;
                    // 保存这个成功连接的ssid和passwd
                    demoks_system_set_wifi_ssid_passwd(json_ssid->valuestring, json_passwd->valuestring);
                    // 告知主线程 WIFI配网成功
                    event_code_0 = DEMOSK_ACCESS_NAT_EVENT_CODE_WIFI_ACC_SUCC;
                    xQueueSend(demoks_access_net_event, (demoks_access_net_event_code_t *)&event_code_0, 0);
                    break;
                case WIFI_DISTRIBUTION_EVENT_CODE_FAIL: /** 连接wifi失败 */
                    code = DEMOSK_ACCESS_NAT_CODE_WIFI_DIS_FAIL;
                    msg = get_msg(code);
                    // 告知主线程 WIFI配网失败
                    event_code_0 = DEMOSK_ACCESS_NAT_EVENT_CODE_WIFI_ACC_FAIL;
                    xQueueSend(demoks_access_net_event, (demoks_access_net_event_code_t *)&event_code_0, 0);
                    break;
                default: /** 未知代码 */
                    break;
                }
            }
        }
        cJSON_Delete(json);
    }
send:
    cJSON_AddNumberToObject(json_data, "code", code); /** 响应码 */
    cJSON_AddStringToObject(json_data, "msg", msg);   /** 响应信息 */
    /* 返回数据 */
    const char *resp_str = cJSON_PrintUnformatted(json_data);
    httpd_resp_send(req, resp_str, strlen(resp_str));
    /** 删除数据 */
    cJSON_Delete(json_data);
    /* After sending the HTTP response the old HTTP request
     * headers are lost. Check if HTTP request headers can be read now. */
    if (httpd_req_get_hdr_value_len(req, "Host") == 0)
    {
        ESP_LOGI(TAG, "Request headers lost");
    }
    wifi_distribution_running = false;
    return ESP_OK;
}

httpd_uri_t post_wifi_distribution = {
    .uri = "/wifi/distribution",
    .method = HTTP_POST,
    .handler = wifi_distribution_post_handler,
    .user_ctx = NULL};

httpd_handle_t start_webserver(void)
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    // 端口号
    config.server_port = DEMOSK_ACCESS_NAT_HTTP_SERVER_PORT;

    // Start the httpd server
    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK)
    {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &get_wifi_ssid);
        httpd_register_uri_handler(server, &get_demoks_info);
        httpd_register_uri_handler(server, &post_wifi_distribution);
        return server;
    }

    ESP_LOGI(TAG, "Error starting server!");
    return NULL;
}

void stop_webserver(httpd_handle_t server)
{
    // Stop the httpd server
    httpd_stop(server);
}

static void disconnect_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data)
{
    httpd_handle_t *server = (httpd_handle_t *)arg;
    if (*server)
    {
        ESP_LOGI(TAG, "Stopping webserver");
        stop_webserver(*server);
        *server = NULL;
    }
}

static void connect_handler(void *arg, esp_event_base_t event_base,
                            int32_t event_id, void *event_data)
{
    httpd_handle_t *server = (httpd_handle_t *)arg;
    if (*server == NULL)
    {
        ESP_LOGI(TAG, "Starting webserver");
        *server = start_webserver();
    }
}

static void handler_event_ap_staconnected(void *arg, esp_event_base_t event_base,
                                          int32_t event_id, void *event_data)
{
    demoks_access_net_event_code_t event_code = DEMOSK_ACCESS_NAT_EVENT_CODE_AP_STACONNECTED;
    xQueueSend(demoks_access_net_event, (demoks_access_net_event_code_t *)&event_code, 0);
}

static void handler_event_ap_stadisconnected(void *arg, esp_event_base_t event_base,
                                             int32_t event_id, void *event_data)
{
    demoks_access_net_event_code_t event_code = DEMOSK_ACCESS_NAT_EVENT_CODE_AP_STADISCONNECTED;
    xQueueSend(demoks_access_net_event, (demoks_access_net_event_code_t *)&event_code, 0);
}

/** 
 *  运行配网服务器 
 */
void ap_http_server_run()
{
    event_wifi_distribution = xQueueCreate(1, sizeof(wifi_distribution_event_code_t));

    // 当用户接入时开启服务
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_START, &connect_handler, &server));
    // 当AP模式断开时关闭服务
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STOP, &disconnect_handler, &server));
    // 站点接入了AP
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STACONNECTED, &handler_event_ap_staconnected, NULL));
    // 站点断开了AP
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STADISCONNECTED, &handler_event_ap_stadisconnected, NULL));
    // 当WIFI获取IP时
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, NULL));
    // 当WIFI连接断开时
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect, NULL));

    server = start_webserver();
}
